package levelEditor;

import breakout2014.LevelFactoryLoader;
import breakout2014.SpatialFactory;
import com.jme3.app.SimpleApplication;
import com.jme3.asset.DesktopAssetManager;
import com.jme3.bounding.BoundingBox;
import com.jme3.input.MouseInput;
import com.jme3.input.controls.ActionListener;
import com.jme3.input.controls.MouseButtonTrigger;
import com.jme3.material.Material;
import com.jme3.material.RenderState;
import com.jme3.math.Vector2f;
import com.jme3.math.Vector3f;
import com.jme3.scene.Node;
import com.jme3.scene.Spatial;
import com.jme3.texture.Texture2D;
import com.jme3.ui.Picture;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;

public class LevelEditor implements ActionListener, EditorDialogInterface{
    
    private static final Logger logger = Logger.getLogger(LevelEditor.class.getName());
    private SimpleApplication context;
    
    private enum myEvent {
    PRESSED_LEFT,
    RELEASED_LEFT
    } 
    private enum myState {
    IDLE,
    SINGLE_SELECTED
    } 
    private myState state = myState.IDLE;
    private Spatial pickedElement = null;
    private Node guiNode;
    private EditorDialog dialog;
    private FileToolset fileTooling;
    private LevelFactoryWriter levelFactoryWriter;
    private LevelEditorFactory levelEditorFactory;

    
    public LevelEditor(SimpleApplication context){
        this.context = context;
        Logger.getLogger(LevelEditor.class.getName()).setLevel(Level.INFO); // SEVERE  INFO
        guiNode  = context.getGuiNode();
        
        // IMPORTANT: Set new screen resolution.
        // call reshape in class Application, although it is mentioned 
        // as internal use only. Reshape will update the Camera object
        // with the correct screen resolution which is important for 
        // nifty. I spend hours to find out...
        context.getContext().getSettings().setResolution(1366,900);
        context.restart();
        context.reshape(1366,900);

        fileTooling = new FileToolset();
        levelFactoryWriter = new LevelFactoryWriter();
        levelEditorFactory = new LevelEditorFactory(context);
        dialog = new EditorDialog(context, this, 400, 8);
        
        // setup input handling
        context.getInputManager().addMapping("mousePick", new MouseButtonTrigger(MouseInput.BUTTON_LEFT));
        context.getInputManager().addListener(this, "mousePick");
    
        levelEditorFactory.bildLevelEditor();
        levelEditorFactory.buildLevel("");
    }

    
    // DIALOG interface functions
    public boolean dialogLoadFile(String fileName){
        levelEditorFactory.bildLevelEditor();
        return levelEditorFactory.buildLevel(fileName);
    }

    public boolean dialogSaveFile(String fileName){
        String levelString = levelFactoryWriter.guiTree2String(guiNode);
        ((DesktopAssetManager)context.getAssetManager()).clearCache();
        return fileTooling.writeLevel(fileName, levelString);
    }
    
    public List<String> dialogGetFileList(){
        FileToolset fileToolset = new FileToolset();
        List<String> list = fileToolset.getFolderList();
        return list;
    }
    
//logger.log(Level.INFO, "mouse: {0}", new Object[]{mousePos.x});
    
    
    // EDITOR GUI Controller
    public void screenUpdate() {
        switch(state){
            case SINGLE_SELECTED:
                Vector2f mousePos = context.getInputManager().getCursorPosition();
                Vector2f mouseOffset = (Vector2f)pickedElement.getUserData("mouseOffset");
                pickedElement.setLocalTranslation(mousePos.x+mouseOffset.x, mousePos.y+mouseOffset.y, 0);
                break; 
        }
    }
    
    public void stateMachine(myEvent event) {
        switch(state){
            case IDLE:
                if(event == myEvent.PRESSED_LEFT){
                    pickedElement = getSpatialAtMousePos("brickBlueprints");
                    if(pickedElement != null){
                        state = myState.SINGLE_SELECTED;
                        Node bricks = (Node)guiNode.getChild("bricks");
                        Spatial clonedSpatial = pickedElement.clone();
                        bricks.attachChild(clonedSpatial);
                        pickedElement = clonedSpatial;
                    }
                    pickedElement = getSpatialAtMousePos("bricks");
                    if(pickedElement != null){
                        state = myState.SINGLE_SELECTED;
                    }
                }
                break; 
        
            case SINGLE_SELECTED:
                if(event == myEvent.RELEASED_LEFT){
                    removeSpatialIfNotOnPlayground(pickedElement);
                    state = myState.IDLE;
                }
                break; 
        }
    }
    
    // check if position of Spatial is inside level area. Remove Spatial if not
    private void removeSpatialIfNotOnPlayground(Spatial element) {
        BoundingBox elementBB = (BoundingBox)element.getWorldBound();
        BoundingBox playgroundBB = levelEditorFactory.getPlaygroundBoundingBox();
        // Check if "element" is enclosed of "playground" completely
        Vector3f elemXLeft = elementBB.getCenter();
        Vector3f elemXRight = elementBB.getCenter();
        Vector3f elemYTop = elementBB.getCenter();
        Vector3f elemYBottom = elementBB.getCenter();
        elemXLeft = elemXLeft.add(elementBB.getXExtent(),0,0);
        elemXRight = elemXRight.subtract(elementBB.getXExtent(),0,0); 
        elemYTop = elemYTop.add(0,elementBB.getYExtent(),0);
        elemYBottom = elemYBottom.subtract(0,elementBB.getYExtent(),0); 
        if(!playgroundBB.intersects(elemXLeft)) element.removeFromParent();
        if(!playgroundBB.intersects(elemXRight)) element.removeFromParent();        
        if(!playgroundBB.intersects(elemYTop)) element.removeFromParent();        
        if(!playgroundBB.intersects(elemYBottom)) element.removeFromParent();        
    }
    
    // interpret mouse button presses and releases
    public void onAction(String name, boolean isPressed, float tpf) {
        if (name.equals("mousePick") && isPressed) {
            stateMachine(myEvent.PRESSED_LEFT);
        }
        if (name.equals("mousePick") && !isPressed) {
            stateMachine(myEvent.RELEASED_LEFT);
        }
    }

    // find out which element in the graph tree is pointed to with the mouse
    public Spatial getSpatialAtMousePos(String parentName) {
        Vector2f mousePos = context.getInputManager().getCursorPosition();
        return findSpatialInParentAt(parentName, mousePos.x, mousePos.y);
    }

    // find spatial at xy coordinates
    public Spatial findSpatialInParentAt(String nodeName, float x, float y) {
        Node node = (Node)guiNode.getChild(nodeName);
        if (node == null) return null;
        for(int i=0; i< node.getQuantity();i++){
            Spatial element = node.getChild(i);
            float xDim = ((BoundingBox)element.getWorldBound()).getXExtent();  
            float yDim = ((BoundingBox)element.getWorldBound()).getYExtent();  
            Vector3f elementPos = element.getLocalTranslation();
            if((x > elementPos.x-xDim) && (x < elementPos.x+xDim) &&
                (y > elementPos.y-yDim) && (y < elementPos.y+yDim)){
                Vector2f mouseOffset = new Vector2f(elementPos.x-x, elementPos.y-y);
                element.setUserData("mouseOffset", mouseOffset);
                return element;
            }
         }    
         return null;
    }
}
